package org.hang.gift.service.impl;

import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.hang.gift.doc.GiftDoc;
import org.hang.gift.es.SearchUtil;
import org.hang.gift.query.GiftQuery;
import org.hang.gift.repository.HighlightResultMapper;
import org.hang.gift.service.IGiftDocService;
import org.hang.util.PageList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.Objects;


@Service
public class GiftDocServiceImpl implements IGiftDocService {


    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    @Autowired
    private HighlightResultMapper highlightResultMapper;

    /**
     * 用户端查询方法，从上往下执行，方法执行前会进行判断，判断条件为空，相关方法就不会执行
     * @param query
     * @return
     */
    @Override
    @Transactional
    public PageList<GiftDoc> searchGifts(GiftQuery query) {
        //获取一个对象:1.构造方法   2.工厂   3.静态方法  4.工具类
        /*SearchQuery是一个接口，只有一个实现类NativeSearchQuery,
        可以先创建NativeSearchQuery的工具类NativeSearchQueryBuilder的对象，
        然后使用 对象.build() 就可以获取NativeSearchQuery
         */
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        //1.数据的分页。 PageRequest.of()方法会返回一个Pageable对象  of(当前页，每页的条数)
        builder.withPageable(PageRequest.of(query.getPage()-1,query.getRows()));

        //2.排序
         /*
          private String sortField; //排序字段
          private String sortType;  //排序类型 desc降序 asc升序
          */
          //2.1 获取排序字段
          String sortField = query.getSortField();
          //排序的工具类，根据某个属性进行排序，通过实例方法order(SortOrder order)来指定是升序还是倒序，默认是升序。
          FieldSortBuilder fieldSortBuilder;
          //2.2 进行判断
          if(StringUtils.isBlank(sortField)){
             //排序字段为空，就默认按 上架时间降序排序
              fieldSortBuilder = new FieldSortBuilder("onsaleTime").order(SortOrder.DESC);
          }else {
             //排序字段不为空，安排序字段排序
              fieldSortBuilder = new FieldSortBuilder(sortField).order("asc".equals(query.getSortType()) ? SortOrder.ASC : SortOrder.DESC);
          }
          builder.withSort(fieldSortBuilder);

          //3. 分组

          //4.查询条件  ES DSL查询 模糊查询
          //创建组合查询对象
          BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
          //关键字查询，查询框不为空
          if(StringUtils.isNotBlank(query.getKeyword())){//不能使用断言工具类，使用了的话查询框为空会报异常
             //multiMatchQuery 多字段分词匹配查询 ，根据名字，品牌名，类型名查询
              boolQuery.must(QueryBuilders.multiMatchQuery(query.getKeyword(),"name","typeName","branName"));
          }
          //5.查询条件  ES DSL过滤 精确查询
          //根据价格区间查询
          if(Objects.nonNull(query.getPriceMin())){
              //spu的最小价格 应该大于区间的最小价格
             boolQuery.filter(QueryBuilders.rangeQuery("minPrice").gte(query.getPriceMin()));
          }
          if(Objects.nonNull(query.getPriceMax())){
            //spu的最大价格 应该小于区间的最大价格
            boolQuery.filter(QueryBuilders.rangeQuery("maxPrice").lte(query.getPriceMax()));
          }

          //6.高亮
          //对搜索的关键字高亮处理  哪个字段要做高亮处理
          //在搜索的关键字 前面 和 后面 加上 html 效果
          HighlightBuilder.Field field1 = new HighlightBuilder.Field("name").preTags("<span style='color:red'>").postTags("</span>");
          HighlightBuilder.Field field2 = new HighlightBuilder.Field("typeName").preTags("<span style='color:red'>").postTags("</span>");
          HighlightBuilder.Field field3 = new HighlightBuilder.Field("branName").preTags("<span style='color:red'>").postTags("</span>");
          builder.withHighlightFields(field1,field2,field3);

          //7,聚合查询，根据类型ID和name 以及根据店铺ID和名字进行分组.并给要将分后的组返回给前端
          //terms 词元查询
          TermsAggregationBuilder typeAggs = AggregationBuilders.terms("typeIdGroup")
                  .field("typeId")
                  .order(BucketOrder.count(false))
                  .subAggregation(
                          AggregationBuilders.terms("typeNameGroup").field("typeName").order(BucketOrder.count(false))
                  );
        TermsAggregationBuilder tenantAggs = AggregationBuilders.terms("tenantIdGroup")
                .field("tenantId")
                .order(BucketOrder.count(false))
                .subAggregation(
                        AggregationBuilders.terms("tenantNameGroup").field("tenantName").order(BucketOrder.count(false))
                );

        builder.addAggregation(typeAggs);
        builder.addAggregation(tenantAggs);

        //AggregatedPage 是 Page 的子类
        //queryForPage(SearchQuery)
        AggregatedPage<GiftDoc> search = elasticsearchTemplate.queryForPage(
                builder.build(), GiftDoc.class, highlightResultMapper);
        //search(SearchQuery),SearchQuery是一个接口，只有一个实现类NativeSearchQuery
        //Page<CarDoc> search = carDocRepository.search(builder.build()); //不支持高亮映射

        //解析统计的结果集,存入map集合
        Map<String, Object> map =
                SearchUtil.handleTermsAggsData(search.getAggregations());
        //获取数据的总条数
        long totalElements = search.getTotalElements();
        //获取当前页面的数据
        List<GiftDoc> content = search.getContent();
        //返回数据
        return new PageList<>(totalElements,content,map);
    }
}
